package org.wat.wcy.isi.mmazur.bp.io.core;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.wat.wcy.isi.mmazur.bp.conf.Config;
import org.wat.wcy.isi.mmazur.bp.io.model.BusinessProcess;
import org.wat.wcy.isi.mmazur.bp.io.model.Node;
import org.wat.wcy.isi.mmazur.bp.io.model.Transition;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.NodeType;
import org.wat.wcy.isi.mmazur.bp.io.model.temporary.MatrixRow;
import org.wat.wcy.isi.mmazur.bp.io.model.transport.BusinessProcessMatrix;
import org.wat.wcy.isi.mmazur.bp.io.parsers.IParser;
import org.wat.wcy.isi.mmazur.bp.io.parsers.ParserFactory;

public class Database {
	
	private static Database instance;
	
	public static Database getInstance() {
		if (instance == null) {
			instance = new Database();
		}
		return instance;
	}

	
	Database() {
	}
	
	public boolean createSchema(final String name) {
		try {
			String dataDir = Config.getInstance().getDataDir();
			File projectDir = new File(dataDir, name);
			projectDir.mkdir();
			BusinessProcess process = new BusinessProcess();
			process.setName(name);
			process.setPath(projectDir.getCanonicalPath());
			if (!existsProcess(name)) {
				IOHandler ioh = new IOHandler();
				List<BusinessProcess> processes = ioh.readProcesses(dataDir);
				if (processes == null) {
					processes = new ArrayList<BusinessProcess> ();
				}
				processes.add(process);
				ioh.saveProcesses(dataDir, processes);
				return true;
			}
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return false;
	}
	
	public void loadModel(final String name, final String fileName) {
		String dataDir = Config.getInstance().getDataDir();
		File projectDir = new File(dataDir, name);
		if (projectDir.exists() && projectDir.isDirectory()) {
			ParserFactory pf = new ParserFactory();
			IParser parser = pf.createParser();
			IDataProvider model = parser.loadModel(projectDir, fileName);
			IOHandler ioh = new IOHandler();
			ioh.store(projectDir, model);
		}
	}

	public BusinessProcessMatrix getMatrix(String processName, boolean flat) {
		if (StringUtils.isEmpty(processName)) {
			return null;
		}
		String projectDir = Config.getInstance().getDataDir() + File.separator + processName;
		IOHandler ioh = new IOHandler();
		List<Node> nodes = ioh.readNodes(projectDir);
		Transition[][] matrix = ioh.readMatrix(projectDir);
		BusinessProcessMatrix res = new BusinessProcessMatrix(nodes, matrix);
		return res;
	}
	
	public int getProcessNodesCount(String processName) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getDataDir() + File.separator + processName;
		return (int) ioh.getNodesCount(projectDir);
	}

	public int getProcessNodesCount(String processId, NodeType nodeType) {
		String projectDir = Config.getInstance().getProjectDir(processId);
		IOHandler ioh = new IOHandler();
		NodeType arr[] = NodeType.values();
		if (arr.length - 1 <= nodeType.ordinal()) {
			return 1;
		}
		int inc = 1;
		NodeType nextType = arr[nodeType.ordinal() + inc];
		int start = ioh.readIndexType(projectDir, nodeType);
		if (start == -1) {
			return 0; //no elements
		}
		int end = -1;
		while (end == -1 && (arr.length - 1 > nextType.ordinal())) {
			end = ioh.readIndexType(projectDir, nextType);
			nextType = arr[nodeType.ordinal() + ++inc];
		}
		return Math.max(0, end - start);
	}

	public int getNextElementsCount(String processId, String id) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		int rowId = ioh.getNodeIndex(projectDir, id);
		MatrixRow result = ioh.readMatrixRow(projectDir, rowId);
		return result.getNotEmptyCount();
	}

	public Map<String, String> getElementAdditionalData(String processId,
			String nodeId) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		Map<String, String> result = ioh.getNodeExtendedData(projectDir, nodeId);
		return result;
	}

	public List<Node> getElementsByRole(String processId, String laneId) {
		List<Node> nodes = getProcessNodes(processId);
		List<Node> result = new ArrayList<Node> ();
		for (Node nd : nodes) {
			if (StringUtils.equalsIgnoreCase(nd.getLaneId().toString(), laneId)) {
				result.add(nd);
			}
		}
		return result;
	}

	public List<Node> getNextElements(String processId, int i) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		MatrixRow res = ioh.readMatrixRow(projectDir, i);
		int index = 0;
		List<Integer> indexes = new ArrayList<Integer>();
		for (Transition d : res.getResult()) {
			if (!d.getId().isNull()) {
				indexes.add(index);
			}
			index++;
		}
		List<Node> result = ioh.readNodes(projectDir, indexes);
		return result;
	}
	
	public List<Node> getPrevElements(String processId, int i) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		MatrixRow res = ioh.readMatrixColumn(projectDir, i);
		int index = 0;
		List<Integer> indexes = new ArrayList<Integer>();
		for (Transition d : res.getResult()) {
			if (!d.getId().isNull()) {
				indexes.add(index);
			}
			index++;
		}
		List<Node> result = ioh.readNodes(projectDir, indexes);
		return result;
	}

	public int getPrevElementsCount(String processId, String id) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		int rowId = ioh.getNodeIndex(projectDir, id);
		MatrixRow result = ioh.readMatrixColumn(projectDir, rowId);
		return result.getNotEmptyCount();
	}

	public List<BusinessProcess> getProcesses() {
		IOHandler ioh = new IOHandler();
		List<BusinessProcess> processes =ioh.readProcesses(Config.getInstance().getDataDir());
		return processes == null ? new ArrayList<BusinessProcess> () : processes;
	}
	
	public List<Node> getProcessNodes(String processId) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		return ioh.readNodes(projectDir);
	}

	public Node getNode(String processId, String name) {
		IOHandler ioh = new IOHandler();
		String projectDir = Config.getInstance().getProjectDir(processId);
		return ioh.readNode(projectDir, name);
	}

	public String getXPDL(String processName) {
		return null;
	}

	public boolean existsProcess(String name) {
		IOHandler ioh = new IOHandler();
		List<BusinessProcess> processes = ioh.readProcesses(Config.getInstance().getDataDir());
		if (processes == null) {
			return false;
		}
		for (BusinessProcess bp : processes) {
			if (StringUtils.equals(bp.getName(), name)) {
				return true;
			}
		}
		return false;
	}

	public boolean removeProcess(String id) {
		String dataDir = Config.getInstance().getDataDir();
		File projectDir = new File(dataDir, id);
		if (projectDir.exists() && projectDir.isDirectory()) {
			try {
				FileUtils.deleteDirectory(projectDir);
				return true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}
}
